package com.wuyou.sso2.handler.login;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import com.wuyou.sso.common.enums.LoginTypeEnum;
import com.wuyou.sso2.vo.UserVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.List;

/**
 * 登录处理器工厂
 *
 * @author lishangbu
 * @date 2021/4/9
 */
@Component
@RequiredArgsConstructor
public class LoginHandlerFactory {

    private final List<LoginHandler> loginHandlers;

    /**
     * 处理方法
     * <ol>
     *     <li>根据登录参数标识(用户凭据@登录类型)解析出登录类型和登录参数</li>
     *     <li>获取登录参数，登录参数不存在的话,获取默认的登录处理器类型(普通客户端登录用户加解密)</li>
     *     <li>默认登录处理器下，强制将用户输入字符串作为登录用户名</li>
     *     <li>校验数据合法性,返回true时继续认证，返回false时不再认证</li>
     *     <li>转换登录参数，如将微信Oauth链接转换为用户标识,普通账号密码将传入的数据直接返回即可</li>
     *     <li>执行登录认证，获取基础数据后，交给Spring Security处理</li>
     * </ol>
     *
     * @param loginStr 登录参数
     * @return 组合后的用户信息，包括用户基础信息、菜单角色权限，额外数据
     */
    public UserVO handle(String loginStr) {
        int index = loginStr.lastIndexOf(StrUtil.AT);
        LoginHandler loginHandler = getDefaultLoginHandler();
        String identify = StrUtil.EMPTY;
        if (index != -1) {
            identify = loginStr.substring(0, index);
            String type = loginStr.substring(index + 1);
            loginHandler = chooseHandler(type);
        }
        // 默认登录处理器下，强制将用户输入字符串作为登录用户名,避免邮箱中也有@符号等造成用户名被截断
        if (loginHandler instanceof DefaultPasswordLoginHandler) {
            identify = loginStr;
        }
        //判空处理
        Assert.notNull(loginHandler, "loginHandler不可为空");
        if (!loginHandler.check(identify)) {
            return null;
        }
        identify = loginHandler.identify(identify);
        return loginHandler.info(identify);
    }

    /**
     * 根据登录参数获取登录处理器，登录参数不存在的话,获取默认的登录处理器类型(普通客户端登录用户加解密)
     *
     * @param type 登录类型的字符串表达
     * @return 一个必定可用的登录处理器
     */
    private LoginHandler chooseHandler(String type) {
        LoginTypeEnum loginTypeEnum = EnumUtil.likeValueOf(LoginTypeEnum.class, type);
        for (LoginHandler handler : loginHandlers) {
            if (loginTypeEnum == null) {
                return getDefaultLoginHandler();
            } else {
                if (handler.support(loginTypeEnum)) {
                    return handler;
                }
            }
        }
        // NOTE:理论上绝对不可能走到这里哒
        return null;
    }

    /**
     * 获取默认登录处理器
     *
     * @return
     */
    private LoginHandler getDefaultLoginHandler() {
        for (LoginHandler loginHandler : loginHandlers) {
            if (loginHandler.support(LoginTypeEnum.PWD)) {
                return loginHandler;
            }
        }
        // NOTE:理论上绝对不可能走到这里
        return null;
    }
}
